using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Text.RegularExpressions;
using System.Diagnostics;
using TXLooker.Common.Core;

namespace TXLooker.WorkItemExtention.WIQL
{
    public class WIQLQuery
    {
        private static string SELECT_REGEX_GROUP_NAME = "select";
        private static string WHERE_REGEX_GROUP_NAME = "where";
        private static string ORDERBY_REGEX_GROUP_NAME = "orderby";
        private static string ASOF_REGEX_GROUP_NAME = "asof";

        private static string WIQL_REGEX_PATTERN =
            string.Format( @"^\s*?SELECT\s+?(?<{0}>\S.*?\S)\s+?FROM\s+?WorkItems\s*?(WHERE\s+?(?<{1}>\S.+?\S))?\s*?(ORDER\s*?BY\s+?(?<{2}>\S.+?\S))?\s*?(ASOF\s+?'(?<{3}>\S.+?\S)')?\s*?$", SELECT_REGEX_GROUP_NAME, WHERE_REGEX_GROUP_NAME, ORDERBY_REGEX_GROUP_NAME, ASOF_REGEX_GROUP_NAME );

        public Guid QueryGUID
        {
            get { return this.m_queryGUID;  }
            set { this.m_queryGUID = value; }
        }
        private Guid m_queryGUID = Guid.Empty;

        public string QueryText
        {
            get { return this.ToString(); }
            internal set
            {
                this.m_queryText = value;
            }
        }
        private string m_queryText = string.Empty;

        public List<WorkItemFieldDefinition> SelectFields
        {
            get { return this.m_selectFields; }
        }
        private List<WorkItemFieldDefinition> m_selectFields = new List<WorkItemFieldDefinition>();


        public List<WorkItemSortField> OrderByFields
        {
            get { return this.m_orderByFields; }
        }
        private List<WorkItemSortField> m_orderByFields = new List<WorkItemSortField>();



        public DateTime AsOf
        {
            get { return this.m_dtASOF;  }
            set { this.m_dtASOF = value; }
        }
        private DateTime m_dtASOF;

        protected string SelectContent
        {
            get 
            {
                StringBuilder builder = new StringBuilder();

                foreach( WorkItemFieldDefinition item in this.SelectFields )
                    builder.AppendFormat( "{0}[{1}]", ( builder.Length == 0 ? string.Empty :", " ), item.ReferenceName );

                return builder.ToString();
            }
        }

        protected string WhereContent
        {
            get { return this.m_whereContent;  }
            set { this.m_whereContent = value; }
        }
        private string m_whereContent = null;

        protected string AsOfContent
        {
            get { return this.m_AsOfContent;  }
            set { this.m_AsOfContent = value; }
        }
        private string m_AsOfContent = null;


        protected string OrderByContent
        {
            get
            {
                StringBuilder builder = new StringBuilder();

                foreach( WorkItemSortField item in this.OrderByFields )
                    builder.AppendFormat( "{0}[{1}]{2}", ( builder.Length == 0 ? string.Empty : ", " ), item.WIFieldDef.ReferenceName, (item.SortType == SortType.Ascending ? " asc" : string.Empty)  );

                return builder.ToString();
            }
        }

        public WIQLQuery( )
        { }
        public WIQLQuery( StoredQuery query ) : this()
        {
            this.Init( query );
        }

        public void Init( StoredQuery query )
        {
            this.Init( query.Project.Store, query.QueryText );
        }
        public void Init( WorkItemStore store, string wiql  )
        {
            WIQLQuery.InitQuery( store, wiql, this );
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="storedQuery"></param>
        /// <param name="query"></param>
        public static void InitQuery( WorkItemStore store, string wiql, WIQLQuery query )
        {
            Regex rg = new Regex( WIQL_REGEX_PATTERN );

            //we must have only one match
            Match match = rg.Match( wiql );

            if( !match.Success )
                return;

            query.QueryText = wiql;
            //query.QueryGUID = storedQuery.QueryGuid;

            //analize query content
            Dictionary<string, string> queryContentMAP = new Dictionary<string, string>();

            string[] groupNames = new string[] { SELECT_REGEX_GROUP_NAME, WHERE_REGEX_GROUP_NAME, ORDERBY_REGEX_GROUP_NAME, ASOF_REGEX_GROUP_NAME };

            foreach( string item in groupNames )
            {
                Group group = match.Groups[item];

                if( group != null && group.Success )
                    queryContentMAP.Add( item, group.Value );
            }

            Dictionary<string, WorkItemFieldDefinition> storeFieldsMAP = new Dictionary<string, WorkItemFieldDefinition>();

            foreach( FieldDefinition item in store.FieldDefinitions )
                storeFieldsMAP.Add( item.ReferenceName, new WorkItemFieldDefinition(item) );

            foreach( string key in queryContentMAP.Keys )
            {
                if( key == SELECT_REGEX_GROUP_NAME )
                {
                    string selectPart = queryContentMAP[key];
                    InitSELECT( selectPart, query, storeFieldsMAP );
                }
                else if( key == WHERE_REGEX_GROUP_NAME)
                {
                    string wherePart = queryContentMAP[key];
                    InitWHERE( wherePart, query, storeFieldsMAP );
                }
                else if( key == ORDERBY_REGEX_GROUP_NAME)
                {
                    string orderByPart = queryContentMAP[key];
                    InitORDERBY( orderByPart, query, storeFieldsMAP );
                }
                else if( key == ASOF_REGEX_GROUP_NAME)
                {
                    string asofPart = queryContentMAP[key];
                    InitASOF( asofPart, query, storeFieldsMAP );
                }                
            }


        }

        private static void InitASOF( string asofPart, WIQLQuery query, Dictionary<string, WorkItemFieldDefinition> storeFieldsMAP )
        {
            query.AsOfContent = asofPart;
        }
        private static void InitWHERE( string wherePart, WIQLQuery query, Dictionary<string, WorkItemFieldDefinition> storeFieldsMAP )
        {
            query.WhereContent = wherePart;
        }
        private static void InitSELECT( string selectPart, WIQLQuery query, Dictionary<string, WorkItemFieldDefinition> storeFieldsMAP )
        {
            string[] fields = selectPart.Split( ',' );

            string FIELD_GROUP = "field";
            Regex rg = new Regex( string.Format(@"\[(?<{0}>.*?)\]", FIELD_GROUP) );

            foreach( string item in fields )
            {
                Match match = rg.Match( item );

                if( !match.Success )
                {
                    Debug.WriteLine( string.Format("Field '{0}' can not be parsed", item) );
                    continue;
                }
                
                Group group = match.Groups[FIELD_GROUP];

                string fieldName = group.Value;

                if( !storeFieldsMAP.ContainsKey(fieldName) )
                {
                    Debug.WriteLine( string.Format("Unknown '{0}' field name", fieldName) );
                    continue;
                }

                WorkItemFieldDefinition fieldDef = storeFieldsMAP[fieldName];
                query.SelectFields.Add( fieldDef );
            }
            
        }
        private static void InitORDERBY( string orderbyPart, WIQLQuery query, Dictionary<string, WorkItemFieldDefinition> storeFieldsMAP )
        {            
            string[] fields = orderbyPart.Split( ',' );

            string FIELD_GROUP = "field";
            string SORT_GROUP = "sort";
            string ASC = "asc";
            string DESC = "desc";

            Regex rg = new Regex( string.Format(@"^\s*?\[(?<{0}>.*?)\]\s*(?<{1}>(({2})|{3}))?\s*$", FIELD_GROUP, SORT_GROUP, ASC, DESC) );

            foreach( string item in fields )
            {
                Match match = rg.Match(item);

                if( !match.Success )
                {
                    Debug.WriteLine( string.Format( "ERROR! Field '{0}' can not be parsed", item ) );
                    continue;
                }

                Group group = match.Groups[FIELD_GROUP];

                if( group == null || (group != null && !group.Success) )
                {
                    Debug.WriteLine( string.Format("Group '{0}' can not be parsed", FIELD_GROUP) );
                    continue;
                }

                string fieldName = match.Groups[FIELD_GROUP].Value;

                if( !storeFieldsMAP.ContainsKey( fieldName ) )
                {
                    Debug.WriteLine( string.Format("Unknown '{0}' field name", fieldName) );
                    continue;
                }

                WorkItemFieldDefinition fieldDef = storeFieldsMAP[fieldName];

                SortType stype = SortType.Descending;

                group = match.Groups[SORT_GROUP];

                if( group != null && group.Success )
                {
                    string sortValue = group.Value.ToLower();

                    if( sortValue == ASC )
                    {
                        stype = SortType.Ascending;
                    }
                    else if( sortValue == DESC )
                    {
                        stype = SortType.Descending;
                    }
                    else
                    {
                        Debug.WriteLine( string.Format("Unknown sort type: '{0}'", sortValue) );
                    }
                }

                WorkItemSortField sortField = new WorkItemSortField( fieldDef, stype );
                query.OrderByFields.Add( sortField );
            }
        }

        public static void Parse( string wigl )
        {
            NodeSelect nodeSelect = Parser.ParseSyntax( wigl );
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();

            string select = this.SelectContent;
            if( string.IsNullOrEmpty( select ) ) throw new Exception("Invalid query select part");

            builder.AppendFormat( "SELECT {0} FROM WorkItems", select );

            string where = this.WhereContent;
            if( !string.IsNullOrEmpty(where) ) builder.AppendFormat( " WHERE {0}", where );

            string orderBy = this.OrderByContent;
            if( !string.IsNullOrEmpty(orderBy) ) builder.AppendFormat( " ORDER BY {0}", orderBy );

            string asof = this.AsOfContent;
            if( !string.IsNullOrEmpty(asof) ) builder.AppendFormat( " ASOF {0}", asof );

            return builder.ToString();
        }
    }
}
